# -*- coding = utf-8 -*-
# @Time : 2023/6/2 14:22
# @Author : 龙王赘婿_彪少
# @File : can_user.py
# @Software: PyCharm

# 功能：创芯科技的can卡调用
# 做完这个可就不得了咯，以后可以编写can测试脚本，接下来还要用Qt做一个，哈哈，还要熟悉canopen协议，最好把ethernet也熟悉了
# 摸了一天鱼了，开搞开搞
# 见接口函数库

# ctypes 库，操作c，c++的库，改天要测试下基本功能
from ctypes import *


# 结构体必须继承ctypes.Structure
# 配置信息结构体
class VCI_INIT_CONFIG(Structure):
    _fields_ = [("AccCode", c_uint),
                ("AccMask", c_uint),
                ("Reserved", c_uint),
                ("Filter", c_ubyte),
                ("Timing0", c_ubyte),
                ("Timing1", c_ubyte),
                ("Mode", c_ubyte)
                ]


# 对象结构体
class VCI_CAN_OBJ(Structure):
    _fields_ = [("ID", c_uint),
                ("TimeStamp", c_uint),
                ("TimeFlag", c_ubyte),
                ("SendType", c_ubyte),
                ("RemoteFlag", c_ubyte),
                ("ExternFlag", c_ubyte),
                ("DataLen", c_ubyte),
                ("Data", c_ubyte * 8),
                ("Reserved", c_ubyte * 3)
                ]


# 接收对象结构体
class VCI_CAN_OBJ_ARRAY(Structure):
    _fields_ = [('SIZE', c_uint16), ('STRUCT_ARRAY', POINTER(VCI_CAN_OBJ))]

    def __init__(self, num_of_structs):
        self.STRUCT_ARRAY = cast((VCI_CAN_OBJ * num_of_structs)(), POINTER(VCI_CAN_OBJ))  # 结构体数组 这个括号不能少
        self.SIZE = num_of_structs  # 结构体长度
        self.ADDR = self.STRUCT_ARRAY[0]  # 结构体数组地址  byref()转c地址


class CAN:
    def __init__(self):
        self.CAN1 = 0
        self.CAN2 = 1
        self.CanDLLName = './ControlCAN.dll'
        self.VCI_USBCAN2 = 4  # 设备类型
        self.STATUS_OK = 1
        self.canDLL = windll.LoadLibrary(self.CanDLLName)
        # canDLL = cdll.LoadLibrary('./libcontrolcan.so') # Linux
        print("can dll name is:", self.CanDLLName)

        # 形参：类型，索引，预留
        ret = self.canDLL.VCI_OpenDevice(self.VCI_USBCAN2, 0, 0)
        if ret == self.STATUS_OK:
            print("打开设备成功！")
        if ret != self.STATUS_OK:
            print("打开设备失败，检查连接线 或 检查can调试助手是否占用")

        # 初始化0通道配置信息
        self.vci_initconfig = VCI_INIT_CONFIG(0x80000008,  # 验收码
                                              0xFFFFFFFF,  # 屏蔽码，表示全部接收验收码
                                              0,  # 保留
                                              0,  # 滤波方式
                                              0x00,  # 波特率定时器0
                                              0x14,  # 波特率定时器1
                                              0  # 模式 0:正常模式 1:只听模式 2:环回模式
                                              )
        # 波特率   定时器0    定时器1
        # 125K    0x03      0x1C
        # 250K    0x01      0x1C
        # 500K    0x00      0x1C
        # 1000K   0x00      0x14

    def init_can(self, can_index):
        # 参数：设备类型 设备索引 CAN通道索引（0：CAN1,1：CAN2）
        ret = self.canDLL.VCI_InitCAN(self.VCI_USBCAN2, 0, can_index, byref(self.vci_initconfig))
        if ret == self.STATUS_OK:
            print(f'配置CAN{can_index+1}成功！')
        if ret != self.STATUS_OK:
            print(f'配置CAN{can_index+1}失败！')

        # 参数：设备类型 设备索引 CAN通道索引（0：CAN1,1：CAN2）
        ret = self.canDLL.VCI_StartCAN(self.VCI_USBCAN2, 0, can_index)
        if ret == self.STATUS_OK:
            print(f'启动CAN{can_index+1}成功!')
        if ret != self.STATUS_OK:
            print(f'启动CAN{can_index+1}失败!')

    def can_send(self, can_index, addr, data):
        ubyte_array = c_ubyte * 8
        buf = ubyte_array(data[0], data[1], data[2], data[3], data[4], data[5], data[6], data[7])
        ubyte_3array = c_ubyte * 3
        reverse = ubyte_3array(0, 0, 0)
        vci_can_obj = VCI_CAN_OBJ(addr,  # 帧地址
                                  0,   # 时间标识
                                  0,   # 是否使用时间标识
                                  1,   # 0:发送失败后4秒重发 1:单次发送
                                  0,   # 0:数据帧 1:远程帧(数据帧为空)
                                  0,   # 0:标准帧(11位ID)， 1:扩展帧(29位ID)
                                  8,   # 数据长度 (<=8)
                                  buf,    # data[8]
                                  reverse  # 系统保留
                                  )
        # 参数：设备类型 设备索引 CAN通道索引(0：CAN1,1：CAN2) 结构体包首指针 包数(建议发一包)
        ret = self.canDLL.VCI_Transmit(self.VCI_USBCAN2, 0, can_index, byref(vci_can_obj), 1)
        if ret == self.STATUS_OK:
            print(f'send CAN{can_index+1}   addr: {addr:#x}', '   data: {}'.format(' '.join(hex(x) for x in data)))
        if ret != self.STATUS_OK:
            print('通道发送失败!')

    def can1_send(self, addr, data):
        self.can_send(self.CAN1, addr, data)

    def can2_send(self, addr, data):
        self.can_send(self.CAN2, addr, data)

    def can_recv(self, can_index):
        self.recv_date = VCI_CAN_OBJ()

        while True:  # 如果没有接收到数据，一直循环查询接收。
            # 参数：设备类型 设备索引 CAN通道索引（0：CAN1,1：CAN2）数组首指针 本次接收最大帧数 保留 返回：实际帧数
            num = self.canDLL.VCI_Receive(self.VCI_USBCAN2, 0, can_index, byref(self.recv_date), 1, 0)
            if num > 0:  # 接收到一帧数据
                print(f'recv CAN{can_index + 1}   addr: {self.recv_date.ID:#x}',
                      '   data: [{}]'.format(', '.join(hex(x) for x in self.recv_date.Data)))

    def can1_recv(self):
        self.can_recv(self.CAN1)

    def can2_recv(self):
        self.can_recv(self.CAN2)


if __name__ == "__main__":
    # 创建对象
    c = CAN()

    # 初始化can口
    c.init_can(c.CAN1)
    c.init_can(c.CAN2)

    # 发送一帧数据
    # addr = 0x77
    # data = [0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88]
    # c.can2_send(addr, data)

    addr_a = 0x60a
    data_a = [0x4, 0xa2, 0x86, 0x0, 0x1, 0x0, 0x0, 0x0]
    addr_b = 0x60b
    data_b1 = [0x3, 0x4e, 0x4c, 0x3, 0x80, 0x20, 0x0, 0xa4]
    data_b2 = [0x0, 0x4e, 0x9c, 0x0, 0x80, 0x20, 0x0, 0xa4]
    data_b3 = [0x1, 0x4f, 0x9c, 0x15, 0x80, 0x20, 0x0, 0x9c]
    data_b4 = [0x2c, 0x51, 0x1b, 0xa4, 0x7e, 0xdf, 0xc0, 0x9a]

    c.can2_send(addr_a, data_a)
    c.can2_send(addr_b, data_b1)
    c.can2_send(addr_b, data_b2)
    c.can2_send(addr_b, data_b3)
    c.can2_send(addr_b, data_b4)

    # c.can1_recv()
    # c.can2_recv()

# self.rx_vci_can_obj = VCI_CAN_OBJ_ARRAY(2500)  # 结构体数组
# num = self.canDLL.VCI_Receive(self.VCI_USBCAN2, 0, can_index, byref(self.rx_vci_can_obj.ADDR), 2500, 0)
# if num > 0:  # 接收到一帧数据
#     print(f'recv CAN{can_index + 1}   addr: {self.rx_vci_can_obj.STRUCT_ARRAY[0].ID:#x}', '   \
#     data: {}'.format(' '.join(hex(x) for x in self.rx_vci_can_obj.STRUCT_ARRAY[0].Data)))
